package com.cry.demo.util;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Copyright 程镕宇版权所有
 * FileName: SqlGenerator
 * DetailedDescription：sql生成工具类 根据 Java实体类自动生成sql语句 --- mysql
 *
 * @author Cheng Rong Yu
 * @version 1.00
 * @Date 2023-02-01 17:49:21
 */
public class SqlGenerator {
    /**
     * 定义空map对象用于存储Java类型与sql类型转换规则
     */
    public static Map javaProperty2SqlColumnMap = new HashMap();

    /**
     * 在类首次装载时进行自定义Java-sql的转换规则
     */
    static {
        javaProperty2SqlColumnMap.put("Integer", "int");
        javaProperty2SqlColumnMap.put("Short", "tinyint(4)");
        javaProperty2SqlColumnMap.put("Long", "bigint");
        javaProperty2SqlColumnMap.put("BigDecimal", "decimal(20,4)");
        javaProperty2SqlColumnMap.put("Double", "double precision not null");
        javaProperty2SqlColumnMap.put("Float", "float");
        javaProperty2SqlColumnMap.put("Timestamp", "datetime");
        javaProperty2SqlColumnMap.put("Date", "datetime");
        javaProperty2SqlColumnMap.put("Boolean", "bit(1)");
        javaProperty2SqlColumnMap.put("String", "varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL");
    }

    /**
     * 用于控制台报错日志使用
     */
    private static final Logger logger = LoggerFactory.getLogger(SqlGenerator.class);

    /**
     * 根据实体类生成建表语句
     * @param className 全类名
     * @param tableName 表名
     */
    public String generateSql(String className, String tableName) throws ClassNotFoundException {
        try {
            /**
             * 通过传入class名称，对象化实体文件
             */
            Class clz = Class.forName(className);
            /**
             * 存放表名
             */
            className = clz.getSimpleName();
            /**
             * 指定返回ApiModel类型注释内容
             */
            ApiModel apiModel = (ApiModel)clz.getAnnotation(ApiModel.class);
            /**
             * 获取表名中文名称
             */
            String tableComment = apiModel.value();
            /**
             * 读取class下所有列 并通过Field反射存储
             */
            Field[] fields = clz.getDeclaredFields();
            /**
             * 存放参数类型
             */
            String param = "";
            /**
             * 存放（字段名）列名
             */
            String column = "";
            /**
             * 存放建表sql
             */
            StringBuilder sql = null;
            /**
             * 初始化对象长度为50 不够时会自增
             */
            sql = new StringBuilder(50);
            /**
             * 当表名为空时
             */
            if (tableName == null || ("").equals(tableName)) {
                /**
                 * 获取TableName注解标注名称
                 */
                TableName tn = (TableName)clz.getAnnotation(TableName.class);
                /**
                 * 读取value
                 */
                tableName = tn.value();
            }
            /**
             * 表名分割
             */
            sql.append("\n\n/*========= "+tableName+" ==========*/\n");
            /**
             * 此操作会执行表删除 默认禁用
             */
//            sql.append("DROP TABLE IF EXISTS `"+className+"`; \n");
            /**
             * 建表语句
             */
            sql.append("CREATE TABLE `").append(tableName).append("` ( \n");
            /**
             * 主键字段名
             */
            String keyField = "";
            /**
             * 主键字段注释
             */
            String keyFieldComment = "";
            /**
             * 循环Field映射字段列表
             */
            for (Field f : fields) {
                /**
                 * 获取表名
                 */
                column = f.getName();
                /**
                 * 获取实体中非数据库字段
                 */
                TableField tableField = f.getAnnotation(TableField.class);
                /**
                 * 默认认定为数据库字段
                 */
                Boolean columnField = false;
                /**
                 * 当TableField对象不为空时
                 */
                if(tableField!=null){
                    /**
                     * 为false时代表非数据库字段
                     */
                    columnField = !tableField.exist();
                }

                /**
                 * 排除uid和分页字段等不属于数据库的字段
                 */
                if (("serialVersionUID").equals(column) || columnField) {
                    continue;
                }
                /**
                 * 获取字段ApiModelProperty注解内容
                 */
                ApiModelProperty nameAnno = f.getAnnotation(ApiModelProperty.class);
                /**
                 * 赋值为注释内容
                 */
                String comment = nameAnno.value();
                /**
                 * 主键注释
                 */
                if("".equals(keyFieldComment)){
                    keyFieldComment = comment;
                }
                /**
                 * 获取Java 字段类型
                 */
                param = f.getType().getSimpleName();

                /**
                 * 将大写字母转小写，并添加下划线
                 */
                for (int i = 0; i < column.length(); i++) {
                    char c = column.charAt(i);
                    if (Character.isUpperCase(c) && i>0) {
                        column = column.replaceAll(
                                Character.toString(c), "_"+Character.toLowerCase(c));
                    }
                }
                /**
                 * sql拼接
                 */
                sql.append("`"+ column +"`");
                /**
                 * 转换类型
                 */
                sql.append(" ").append(javaProperty2SqlColumnMap.get(param));
                /**
                 * 默认第一个是主键
                 */
                if ("".equals(keyField)) {
                    keyField = column;
                }
                /**
                 * 添加注释
                 */
                sql.append(" COMMENT '"+comment+"'");
                sql.append(",\n");
            }
            /**
             * 设定主键 并使用USING BTREE建立索引
             */
            sql.append("PRIMARY KEY (`"+keyField+"`) USING BTREE,");
            sql.append("\nINDEX `"+keyField+"`(`"+keyField+"`) USING BTREE ) ");
            /**
             * utf8编码
             */
            sql.append("\nENGINE = INNODB DEFAULT CHARSET= utf8;");
            /**
             * 主键自增并加注释
             */
            sql.append("\nalter table "+tableName+" modify "+keyField+" int auto_increment COMMENT '"+keyFieldComment+"';");
            /**
             * 表注释
             */
            sql.append("\nALTER TABLE "+tableName+" COMMENT '"+tableComment+"';");
            return sql.toString();

        } catch (ClassNotFoundException e) {
            logger.debug("该类未找到！"+e.getException());
            return null;
        }
    }

    /**
     * 获取包下的所有类名称,获取的结果类似于 XXX.class
     * @author
     * @date
     * @param packageName 包名路径
     * @return
     */
    public List getAllClasses(String packageName){
        List classList = new ArrayList();
        String className="";
        File f = new File(packageName);
        if(f.exists() && f.isDirectory()){
            File[] files = f.listFiles();
            for (File file : files) {
                className = file.getName();
                classList.add(className);
            }
            return classList;
        }else{
            logger.debug("包路径未找到！");
            return null;
        }
    }
    /**
     * 将string 写入sql文件
     * @author
     * @date
     * @param str
     * @param path
     */
    public void StringToSql(String str,String path){
        byte[] sourceByte = str.getBytes();
        if(null != sourceByte){
            try {
                /**
                 * 文件路径（路径+文件名）
                 */
                File file = new File(path);
                /**
                 * 文件不存在则创建文件，先创建目录
                 */
                if (!file.exists()) {
                    File dir = new File(file.getParent());
                    dir.mkdirs();
                    file.createNewFile();
                }
                /**
                 * 文件输出流用于将数据写入文件
                 */
                FileOutputStream outStream = new FileOutputStream(file);
                outStream.write(sourceByte);
                outStream.flush();
                outStream.close();
                System.out.println("生成成功！");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
